package com.blockchain.common.util;

import cn.bubi.access.adaptation.blockchain.bc.response.Account;
import cn.bubi.access.adaptation.blockchain.bc.response.TransactionHistory;
import cn.bubi.access.adaptation.blockchain.bc.response.operation.SetMetadata;
import cn.bubi.access.utils.blockchain.BlockchainKeyPair;
import cn.bubi.access.utils.blockchain.SecureKeyGenerator;
import cn.bubi.access.utils.spring.Assert;
import cn.bubi.sdk.core.exception.SdkException;
import cn.bubi.sdk.core.operation.OperationFactory;
import cn.bubi.sdk.core.operation.impl.CreateAccountOperation;
import cn.bubi.sdk.core.operation.impl.IssueAssetOperation;
import cn.bubi.sdk.core.operation.impl.SetMetadataOperation;
import cn.bubi.sdk.core.spi.BcOperationService;
import cn.bubi.sdk.core.spi.BcOperationServiceImpl;
import cn.bubi.sdk.core.spi.BcQueryService;
import cn.bubi.sdk.core.transaction.Transaction;
import cn.bubi.sdk.core.transaction.model.Signature;
import cn.bubi.sdk.core.transaction.model.TransactionCommittedResult;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.blockchain.common.bean.BlockChainResult;
import com.blockchain.common.bean.BuildAccount;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Map;

@Slf4j
public class ChainUtils {

    private static final String defaultJs = "function main(input) { /*do what ever you want*/ }";

    private static String defaultSinger;

    public static Account getAccount(String accountHash, BcQueryService bcQueryService) {
        return bcQueryService.getAccount(accountHash);
    }

    /**
     * @param account 签名用户（业务定义ower）
     * @return
     */
    public static BlockChainResult createAccount(BuildAccount account, BcOperationService bcOperationService) throws SdkException {

        Map<String, String> metaDatas = account.getMetaDatas();
        String meta = account.getMeta();
        String script = account.getScript();
        List<Signature> signatures = account.getSignatures();
        String signer = account.getSigner();

        Transaction transaction = null;

        BlockchainKeyPair keyPair = SecureKeyGenerator.generateBubiKeyPair();
        log.info(JSON.toJSONString(keyPair));
        if (signatures == null || signatures.isEmpty()) {
            transaction = bcOperationService.newTransactionByAccountPool();
        } else {
            transaction = bcOperationService.newTransaction(defaultSinger);
            for (Signature signature : signatures) {
                transaction = transaction.buildAddSigner(signature.getPublicKey(), signature.getPrivateKey());
            }
        }
        CreateAccountOperation.Builder builder = new CreateAccountOperation.Builder();
        for (Map.Entry<String, String> entry : metaDatas.entrySet()) {
            builder = builder.buildAddMetadata(entry.getKey(), entry.getValue());
        }

        CreateAccountOperation createAccountOperation = builder.buildDestAddress(keyPair.getBubiAddress())
                .buildScript(StringUtils.isEmpty(script) ? defaultJs : script).buildPriMasterWeight(15)
                .buildPriTxThreshold(15)
                /**
                 * 账户的metadata（元数据）为一个key-value格式的数据，一个账户可以有多个这个的key-value信息，可以用来存储一些
                 * 备注或标识信息，比如存储创建该账户的账户地址。
                 */
                .buildAddMetadata("账户基本信息", meta).build();

        // 签名完成之后可以继续提交,需要自己维护transaction保存
        TransactionCommittedResult result = transaction.buildAddOperation(createAccountOperation).commit();

        BlockChainResult blockChainResult = new BlockChainResult();
        blockChainResult.setBlockchainKeyPair(keyPair);
        blockChainResult.setTransactionCommittedResult(result);
        return blockChainResult;
    }

    /**
     * 创建一般账号
     *
     * @param initiatorAddress 创世账号地址
     * @param publicKey        创世账号公钥
     * @param privateKey       创世账号私钥
     * @param map              meta数据
     */
    public static BlockChainResult createSimpleAccount(String initiatorAddress, String publicKey, String privateKey,
                                                Map<String, String> map, BcOperationService bcOperationService) throws SdkException {
        // 新建交易对象
        Transaction transaction = bcOperationService.newTransaction(initiatorAddress);
        // 生成密钥对
        BlockchainKeyPair keyPair = SecureKeyGenerator.generateBubiKeyPair();
        // 创建一个生成用户操作
        CreateAccountOperation createAccountOperation = new CreateAccountOperation.Builder()
                .buildDestAddress(keyPair.getBubiAddress()).buildPriMasterWeight(15) // 设置账户自身权重
                .buildPriTxThreshold(15) // 设置账户默认门限
                .buildAddMetadata("基本信息", map.toString()) // 设置元数据（用户根据需要自定义）
                .build();

        transaction.buildAddOperation(createAccountOperation);

        transaction.buildAddSigner(publicKey, privateKey);
        TransactionCommittedResult result = transaction.commit();

        BlockChainResult blockChainResult = new BlockChainResult();
        blockChainResult.setBlockchainKeyPair(keyPair);
        blockChainResult.setTransactionCommittedResult(result);
        return blockChainResult;
    }

    /**
     * 发行资产
     *
     * @param user1
     * @param assetCode
     * @param transferAmount 交易金额 （单位：分）
     * @return 交易hash
     */
    public static String issueOperation(BlockchainKeyPair user1, String assetCode, long transferAmount, BcOperationService bcOperationService) throws SdkException {
        log.info("user1:{}", JSONObject.toJSONString(user1));
        Transaction issueTransaction = bcOperationService.newTransaction(user1.getBubiAddress());
        issueTransaction
                .buildAddOperation(new IssueAssetOperation.Builder().buildAmount(transferAmount)
                        .buildAssetCode(assetCode).build())
                .buildAddSigner(user1.getPubKey(), user1.getPriKey()).commit();
        return issueTransaction.getTransactionBlob().getHash();
    }

    /**
     * 转移资产，交易
     *
     * @param user1
     * @param user2
     * @param assetCode
     * @param transferAmount 交易金额 （单位：分）
     * @return 交易hash
     */
    public static String assetOperation(BlockchainKeyPair user1, String user2, String assetCode, long transferAmount, BcOperationService bcOperationService
            , BcQueryService bcQueryService) throws SdkException {

        log.info("user1:{}", JSONObject.toJSONString(user1));
        Account account = bcQueryService.getAccount(user1.getBubiAddress());
        log.info("user1资产:" + JSON.toJSONString(account.getAssets()));
        Assert.notNull(account.getAssets(), "发行资产不能为空");
        Transaction transferTransaction = bcOperationService.newTransaction(user1.getBubiAddress());
        transferTransaction.buildAddOperation(
                OperationFactory.newPaymentOperation(user2, user1.getBubiAddress(), assetCode, transferAmount))
                .buildAddSigner(user1.getPubKey(), user1.getPriKey()).commit();
        String transHash = transferTransaction.getTransactionBlob().getHash();
//            Account account2 = bcQueryService.getAccount(user2);
//            log.info("account2:" + GsonUtil.toJson(account2));
//
//            log.info("user2资产:" + GsonUtil.toJson(account2.getAssets()));
//            Assert.notNull(account2.getAssets(), "转移资产没有收到");
//            Assert.isTrue(transferAmount == account2.getAssets()[0].getAmount(), "转移资产数量错误");
        return transHash;
    }

    public static BlockChainResult excuteContract(BlockchainKeyPair contractStarter, BlockchainKeyPair contractUser, String jsonString
            , BcOperationService bcOperationService, BcQueryService bcQueryService) throws SdkException {
        TransactionCommittedResult result = bcOperationService.newTransaction(contractStarter.getBubiAddress())

                .buildAddOperation(
                        OperationFactory.newInvokeContractOperation(contractUser.getBubiAddress(), jsonString))
                .commit(contractStarter.getPubKey(), contractStarter.getPriKey());
        TransactionHistory transactionHistory = bcQueryService.getTransactionHistoryByHash(result.getHash());
        BlockChainResult blockChainResult = new BlockChainResult();
        blockChainResult.setTransactionCommittedResult(result);
        blockChainResult.setTransactionHistory(transactionHistory);
        return blockChainResult;

    }

    /**
     * 发行资产2
     *
     * @param issuerAddress    发行人的地址
     * @param issuerPublicKey  公钥
     * @param issuerPrivateKey 私钥
     * @param issueAssetCode
     * @param issueAmount
     * @return
     */
    public static String IssueAsset(String issuerAddress, String issuerPublicKey, String issuerPrivateKey,
                             String issueAssetCode, long issueAmount, BcOperationService bcOperationService) throws SdkException {
        // 创建一个交易
        Transaction issueTransaction = bcOperationService.newTransaction(issuerAddress);
        // 创建发行资产操作，设置发行量和资产编码
        IssueAssetOperation issueAssetOperation = OperationFactory.newIssueAssetOperation(issueAssetCode,
                issueAmount);
        issueTransaction.buildAddOperation(issueAssetOperation);
        issueTransaction.buildAddSigner(issuerPublicKey, issuerPrivateKey);
        issueTransaction.commit();
        return issueTransaction.getTransactionBlob().getHash();
    }

    /**
     * 查询交易
     * 此接口用于通过一个交易的hash值查询该交易的信息，通过调用BcQueryService的getTransactionHistoryByHash方法来获取账户的信息。
     * 要查询的交易的hash值
     */
    public static TransactionHistory getTransaction(String transactionHash, BcQueryService bcQueryService) {
        TransactionHistory txInfo = bcQueryService.getTransactionHistoryByHash(transactionHash);
        return txInfo;
    }

    /**
     * 新增/修改元数据
     *
     * @param address  地址
     * @param pubkey   公钥
     * @param prikey   私钥
     * @param metaData 内容信息
     */
    public static String addMetadata(String address, String pubkey, String prikey, String ordernoKey, String metaData,
                              BcOperationService bcOperationService, BcQueryService bcQueryService) throws SdkException {
        SetMetadata setMetadata = getMetadataInfo(address, ordernoKey, bcQueryService);
        if (setMetadata != null) {
            try {
                // 设置修改后的value值
                setMetadata.setValue(metaData);
                // 新建交易对象
                Transaction updateMetadataTransaction = bcOperationService.newTransaction(address);
                // 创建修改metadata操作对象并进行签名提交
                SetMetadataOperation updataSetMetadataOperation = OperationFactory
                        .newUpdateSetMetadataOperation(setMetadata);
                updateMetadataTransaction.buildAddOperation(updataSetMetadataOperation);
                updateMetadataTransaction.buildAddSigner(pubkey, prikey);
                TransactionCommittedResult transactionCommittedResult = updateMetadataTransaction.commit();
                return transactionCommittedResult.getHash();
            } catch (SdkException e) {
                e.printStackTrace();
            }

        } else {
            // 要新增metadata的账户信息
            Transaction newMetadataTransaction = bcOperationService.newTransaction(address);
            try {
                SetMetadataOperation setMetadataOperation = OperationFactory.newSetMetadataOperation(ordernoKey,
                        metaData);
                newMetadataTransaction.buildAddOperation(setMetadataOperation);
                newMetadataTransaction.buildAddSigner(pubkey, prikey);
                TransactionCommittedResult transactionCommittedResult = newMetadataTransaction.commit();
                return transactionCommittedResult.getHash();
            } catch (SdkException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public static SetMetadata getMetadataInfo(String address, String opnumKey, BcQueryService bcQueryService) {
        // String key = "该账户的创建人地址";//要查询的metadata的key值
        SetMetadata metadata = bcQueryService.getAccount(address, opnumKey);
        // System.out.println(JSON.toJSONString(metadata));
        return metadata;
    }

//	public static void main(String[] args) {
//		// Keypair keypair = Keypair.generator();
//		BlockchainKeyPair keyPair = SecureKeyGenerator.generateBubiKeyPair();
//		System.out.println(keyPair.getPriKey());
//		System.out.println(keyPair.getPubKey());
//		System.out.println(keyPair.getBubiAddress());
//
//	}
}
